Skip to content

Add spanmetricsconnector for RED metrics from Datadog traces - #3044

Merged
kodiakhq[bot] merged 11 commits into
ClickHouse:mainfrom
arj22:feat/add-spanmetrics-connector
Sep 6, 2026
Merged

kodiakhq[bot] merged 11 commits into
ClickHouse:mainfrom
arj22:feat/add-spanmetrics-connector

Conversation

@arj22

@arj22 arj22 commented Sep 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Adds the OpenTelemetry spanmetricsconnector to HyperDX's custom collector build.
  • Enables users to derive span-based RED metrics — request count, errors, and latency — from ingested traces (most useful for Datadog Agent traces, where these metrics aren't otherwise available in the current HyperDX ingestion path).
  • Supports exponential histograms, which HyperDX already stores natively.
  • The change is additive and opt-in; it does not modify any default collector pipelines or runtime behavior.
  • Documents how to wire the connector in both standalone and OpAMP-managed deployments.
  • Includes the required changeset for @hyperdx/otel-collector.

Test plan

  • Built the collector using the repo's documented Docker build flow.
  • Verified spanmetricsconnector v0.155.0 is present in the resulting binary.
  • Validated the documented standalone and OpAMP configurations against the compiled collector.
Implementation notes / OpAMP wiring details

In OpAMP mode, receivers/exporters on the default traces/metrics pipelines are supplied dynamically and overwrite the corresponding values from CUSTOM_OTELCOL_CONFIG_FILE. Because of that, span-metrics wiring must use separate pipeline names rather than modifying the default pipelines.

The documented config sets a few fields beyond the bare minimum: namespace (pins the connector's own default for explicitness), histogram.exponential.max_size (opts into the exponential histogram this PR is motivated by, vs. the connector's default explicit-bucket histogram), aggregation_cardinality_limit + series_expiration (bounds the connector's own in-memory state, which memory_limiter doesn't cover), and aggregation_temporality: DELTA (cumulative temporality plus expiration can reset a low-traffic series' counter in a way HyperDX's rate SQL doesn't detect; delta avoids the issue).

Two specific claims came up more than once across review and were checked directly against the pinned v0.155.0 source and the compiled binary — both turned out to be incorrect, noted here so they aren't re-litigated: the connector's canonical config key is span_metrics (spanmetrics is a working deprecated alias, not the other way around), and series_expiration is a real, distinct field from metrics_expiration.

Follow-up

An end-to-end smoke test that emits a span and asserts generated calls/duration metrics reach ClickHouse would provide additional coverage beyond collector config validation.

The datadogreceiver contrib component is already compiled in and
documented (README's "Ingesting Datadog traces, metrics, and logs")
for accepting Datadog Agent traces, metrics, and logs directly into
HyperDX. There's no way to turn those ingested traces into span-derived
RED metrics (call counts, error rates, latency) once they land, since
spanmetricsconnector isn't part of the build.

This is a common downstream need for anyone using Datadog Agent traces
today: the same span data commonly needs both a "calls" counter and a
duration histogram broken out by service/operation/status, computed
once at the collector rather than per-dashboard-query. Latency
specifically benefits from spanmetricsconnector's exponential
histogram support (histogram.exponential.max_size), which HyperDX's
ClickHouse schema already stores natively via the dedicated
otel_metrics_exponential_histogram table - no schema changes needed on
that side.

Purely additive and opt-in, like the existing routingconnector: being
compiled in adds no runtime behavior on its own, only availability for
a user's own pipeline config (CUSTOM_OTELCOL_CONFIG_FILE in OpAMP mode,
or a standalone config) to wire it in, the same pattern the README
already documents for datadogreceiver itself ("add a datadog receiver
block to your collector config").

Verified against this repo's own documented build process (README's
"Step 3: Verify"): `docker build -f docker/otel-collector/Dockerfile
--target dev` succeeds, and the resulting binary's `components` output
confirms spanmetricsconnector v0.155.0 is compiled in.
@vercel

vercel Bot commented Sep 1, 2026

Copy link
Copy Markdown

@arj22 is attempting to deploy a commit to the HyperDX Team on Vercel.

A member of the Team first needs to authorize it.

@changeset-bot

changeset-bot Bot commented Sep 1, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 83f867f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@hyperdx/otel-collector Minor
@hyperdx/api Minor
@hyperdx/app Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@greptile-apps

greptile-apps Bot commented Sep 1, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR compiles the OpenTelemetry span-metrics connector and documents opt-in standalone and OpAMP configurations for deriving RED metrics from traces.

  • Adds spanmetricsconnector to the custom collector build.
  • Documents bounded delta-temporality exponential histogram configurations.
  • The standalone example currently replaces the native Datadog metrics route when copied by an existing Datadog user.
  • Adds the required collector package changeset.

Confidence Score: 4/5

The PR is not yet safe to merge because the standalone configuration can silently disable native Datadog metric ingestion.

The explicit metrics receiver list replaces the standalone pipeline's existing receivers, so users who already configured Datadog and copy the example retain trace-derived metrics but lose Datadog's native metrics route.

Files Needing Attention: packages/otel-collector/README.md

Important Files Changed

Filename Overview
packages/otel-collector/README.md Documents standalone and OpAMP span-metrics wiring, but the standalone metrics receiver override drops configured native Datadog metric ingestion.
packages/otel-collector/builder-config.yaml Adds the pinned spanmetricsconnector module to the custom collector build.
.changeset/spanmetrics-connector.md Records the additive connector capability as a minor collector package release.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  DD[Datadog receiver] --> T[Traces pipeline]
  T --> SM[span_metrics connector]
  SM --> M[Metrics pipeline]
  M --> CH[ClickHouse]
  DD -. native metrics route replaced by example .-> M
Loading

Fix all with Greploop Fix All in Claude Code Fix All in Conductor Fix All in Cursor Fix All in Codex

Reviews (10): Last reviewed commit: "Merge branch 'main' into feat/add-spanme..." | Re-trigger Greptile

@github-actions

github-actions Bot commented Sep 1, 2026 •

Copy link
Copy Markdown
Contributor

PR Review

3 finding(s): 🔴 0 critical · 🟠 0 major · 🔵 3 minor

3 posted as inline comment(s) on the changed lines.


Severity is the reviewer's own estimate and is used for ordering, not filtering.

@github-actions

github-actions Bot commented Sep 1, 2026 •

Copy link
Copy Markdown
Contributor

Deep Review

Scope: 3 files — packages/otel-collector/builder-config.yaml (compiles in spanmetricsconnector), .changeset/spanmetrics-connector.md (new minor changeset), packages/otel-collector/README.md (new ## Computing span metrics from ingested traces section). Base cff6388.

Intent: Add the OTel spanmetricsconnector to the collector build (purely additive, opt-in) and document standalone- and OpAMP-mode wiring to derive RED metrics from ingested traces. No default pipeline or runtime behavior changes.

✅ No critical issues found. The only executable change is a one-line, additive module entry in builder-config.yaml that matches its sibling connector entries and uses the shared v__OTEL_COLLECTOR_VERSION__ placeholder; it alters no default pipeline. The remainder is documentation that has already been through several rounds of review with the concrete config bugs (missing connectors: block, unconditional datadog receiver, cardinality/expiration/namespace/temporality/histogram fields, batch placement, minor bump) fixed and re-validated against the compiled binary. Independent checks confirm the doc's in-repo cross-references resolve and its technical claims hold: getExpressions exists in serviceDashboard.ts, and renderChartConfig.ts does branch on AggregationTemporality = 1 to pass DELTA Value through while cumulative uses greatest(Value - lagInFrame(...), 0) — matching the rationale given for choosing delta temporality.

🟡 P2 — recommended

  • packages/otel-collector/builder-config.yaml:138 — No smoke test covers the newly documented span→calls/duration wiring, even though the harness to do it already exists (smoke-tests/otel-collector/custom-pipeline-config.yaml + custom-pipeline-processors.bats load a CUSTOM_OTELCOL_CONFIG_FILE and assert wiring), so nothing guards the two config examples against silent breakage on version bumps; binary components/validate only prove the module compiled and the schema parses.
    • Fix: Add a bats fixture that emits a span through the documented span_metrics wiring and asserts a calls/duration row lands in ClickHouse.
🔵 P3 nitpicks (1)
  • packages/otel-collector/README.md:170 — The section pins detailed rationale to spanmetricsconnector v0.155.0 internals (canonical span_metrics type vs deprecated spanmetrics, DefaultNamespace, series_expiration vs metrics_expiration semantics), which will silently drift from reality when the collector's pinned version bumps, since nothing ties the prose to the actual version in builder-config.yaml.
    • Fix: Note that the field-level claims are anchored to the currently pinned collector version so a future reader knows to re-verify them after a version bump.

Reviewers (6): correctness, testing, maintainability, project-standards, previous-comments, orchestrator-verification (in-repo cross-reference and temporality-SQL checks).

Testing gaps:

  • No smoke/integration test asserts the documented wiring produces metric rows in ClickHouse (harness exists; author acknowledged as a follow-up).
  • The span_metrics config-key claim (canonical vs the deprecated spanmetrics alias) rests on the author's documented otelcontribcol validate --config and binary-start run for OTel v0.155.0; it is not independently reproducible in this review environment.

The README's "Included components" section is the canonical 1:1
inventory of every compiled component, but the Connectors table
only listed forward and routing, leaving the newly-compiled
spanmetricsconnector undiscoverable via the doc that's supposed to
advertise it.
Comment thread packages/otel-collector/README.md Resolved
Verified against opampController.ts and config.yaml's own comment: in
OpAMP supervisor mode, receivers/exporters on the default traces/metrics
pipelines are set dynamically and overwrite (not merge with) whatever
CUSTOM_OTELCOL_CONFIG_FILE adds there - the same mechanism already
documented for why processors: is kept out of the remote config. Adding
spanmetrics to those pipelines in OpAMP mode is silently dropped, no
error. Documented the working pattern (separate, non-colliding pipeline
names) for OpAMP mode, alongside the direct approach that does work in
standalone mode.
Comment thread packages/otel-collector/README.md Resolved
Comment thread packages/otel-collector/README.md Resolved
Comment thread packages/otel-collector/README.md Resolved
- gomod:
github.com/open-telemetry/opentelemetry-collector-contrib/connector/routingconnector
v__OTEL_COLLECTOR_VERSION__
- gomod:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — No smoke test for the documented spanmetrics wiring, though the fixture harness already exists

smoke-tests/otel-collector/ already runs an otel-collector-custom service that loads custom-pipeline-config.yaml via CUSTOM_OTELCOL_CONFIG_FILE and asserts pipeline wiring took effect (custom-pipeline-processors.bats). A parallel fixture + bats test that emits a span and asserts a calls/duration row lands in ClickHouse would cost a few lines and would have caught the missing connectors: block above; add one rather than relying on components output, which only proves the module was compiled in.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point, real suggestion — didn't add the full bats smoke test yet (it wasn't as clearly a must-have as the two config bugs above, and a real cross-signal trace→metric test is a bit more involved than the existing same-signal fixtures). What I did instead, given the same underlying goal of "prove the wiring actually works" rather than just components output: validated both corrected snippets against the real compiled binary via otelcontribcol validate --config, including reproducing both failure modes from your other comments verbatim before fixing them. That covers config-shape correctness but not an actual emitted span landing as a calls/duration row in ClickHouse, which your suggested test would catch. Happy to add the bats test as a follow-up if you'd still like it — didn't want to assume the scope without checking first.

Comment thread packages/otel-collector/README.md Resolved
Three real bugs in the examples added last commit, all confirmed by
running otelcontribcol validate --config against the actual compiled
binary rather than just fixing on faith:

- Missing top-level `connectors:` block. A connector referenced only in
  a pipeline's receivers/exporters fails to start:
  "service::pipelines::traces: references exporter \"spanmetrics\"
  which is not configured" - reproduced verbatim.
- OpAMP example referenced the `datadog` receiver unconditionally, but
  it only exists when ENABLE_DATADOG_RECEIVER=true. Reproduced the
  exact failure this causes too: an undefined receiver fails the
  entire config's validation, not just the pipeline that names it.
- Missing `processors: [memory_limiter, batch]`, the convention every
  other user-added pipeline in this repo follows.

Also promoted the section from a Datadog-nested `###` to its own `##`
- spanmetrics works with any trace source, not just Datadog, and was
  unfindable for OTLP-only users nested under the Datadog auth
  subsection.

Both corrected shapes pass `otelcontribcol validate --config` cleanly.
Comment thread packages/otel-collector/README.md Resolved
Comment thread packages/otel-collector/README.md Resolved
Comment thread packages/otel-collector/README.md Resolved
Three more real issues, all verified against the actual compiled
binary rather than fixed on faith:

- spanmetrics aggregates unbounded by default, and memory_limiter
  doesn't protect its internal dimension map (that only gates incoming
  pipeline data, not a connector's own separate state) - on a
  Datadog-firehose source, high-cardinality span names would grow that
  map until OOM regardless of memory_limiter's own setting. Added
  aggregation_cardinality_limit to both examples. Confirmed the field
  name is real (not guessed) by validating it against the compiled
  binary, and separately confirmed `validate` does real strict field
  checking by watching it reject a made-up field name.

- With no `namespace`, the connector emits bare `calls`/`duration`
  metric names into the same shared otel_metrics_sum/otel_metrics_histogram
  tables every other source writes to, keyed by MetricName - a real
  collision risk. Added `namespace: traces.span.metrics` to both
  examples, matching the naming convention already reverse-engineered
  from a real Chronosphere gateway config.

- The claim that processors: [memory_limiter, batch] matters on all
  four example pipelines didn't hold: standalone traces/metrics
  already inherit it from the base config (re-declaring is a no-op,
  per this README's own "only re-declare what you're retuning"
  convention), and OpAMP's traces/spanmetrics has no ClickHouse
  exporter, so batching does nothing there. Removed the redundant
  re-declaration from the standalone example and rescoped the prose to
  what's actually true. Verified the standalone example still merges
  and validates correctly without the redundant processors line, using
  the same multi --config invocation the real container uses (base
  config.yaml + standalone-config.yaml + this example).
Comment thread packages/otel-collector/README.md Resolved
Comment thread packages/otel-collector/README.md Resolved
Comment thread packages/otel-collector/README.md Resolved
Comment thread .changeset/spanmetrics-connector.md Resolved
- Standalone example: re-list `datadog` in both traces/metrics receiver
  lists, since confmap replaces pipeline lists wholesale rather than
  appending - previously silently dropped Datadog metric ingestion for
  anyone following the earlier datadogreceiver section too. Verified
  fixed shape validates cleanly via otelcontribcol validate --config
  (exit 0, with datadog receiver actually defined).
- Add metrics_expiration: 5m alongside aggregation_cardinality_limit in
  both examples - the connector never expires aggregations by default,
  so a cap alone converts an OOM risk into permanent silent data loss
  for new series once a burst fills the map. Verified as a real field
  via validate (exit 0).
- Drop `batch` from OpAMP example's traces/spanmetrics pipeline (keep
  memory_limiter only) - that pipeline's only exporter is the connector
  itself, not network I/O, so batching adds latency/memory for no
  benefit. Verified still validates cleanly with batch removed.
- Changeset: patch -> minor, matching the datadogreceiver precedent
  (CHANGELOG.md 2.30.0 - a compiled-in opt-in component addition is a
  minor bump).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Comment thread packages/otel-collector/README.md Resolved
Comment thread packages/otel-collector/README.md Resolved
Comment thread packages/otel-collector/README.md Resolved
…rics examples

Both spanmetrics examples referenced `datadog` unconditionally in a
receiver list, guarded only by a comment - copying either example
verbatim without separately enabling/configuring Datadog broke config
validation entirely (`references receiver "datadog" which is not
configured`, failing every pipeline, not just the one referencing it).
Removed `datadog` from both executable examples and moved the
conditional-add guidance into a comment on the line it actually
applies to. Verified both corrected examples validate cleanly (exit 0)
against the real compiled binary, using the same multi --config merge
order the container actually uses (bootstrap + standalone-config +
custom for the standalone case; bootstrap + custom + a reproduction of
opampController.ts's real remote-config shape for the OpAMP case).

Also re-verified the disputed "silently dropped, no error" OpAMP
pitfall claim by building that exact bootstrap+custom+remote merge
with spanmetrics wired into the default traces/metrics pipelines: it
validates with exit 0, and a separate control test (a connector used
as an exporter but never as a receiver) does produce a real error
(`connector "spanmetrics" used as exporter in [traces] pipeline but
not used in any supported receiver pipeline`), confirming validate
does perform genuine connector-usage checks and that the merged,
fully-unreferenced case is a real silent no-op, not a validation gap.
No prose change needed there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Comment thread packages/otel-collector/README.md Resolved
Comment thread packages/otel-collector/README.md Resolved
processors: [memory_limiter, batch]
```

Both shapes above are verified against the real compiled binary via

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — Documented wiring is only verified by validate --config, though a smoke-test harness for exactly this pattern already exists

otelcontribcol validate --config proves the YAML parses and every component name resolves; it does not prove a span produces a calls/duration row in ClickHouse, which is the whole point of the section. The repo already has the harness for this: smoke-tests/otel-collector/custom-pipeline-processors.bats + custom-pipeline-config.yaml + the otel-collector-custom service (smoke-tests/otel-collector/docker-compose.yaml:83, wired into setup_suite.bash) cover the analogous documented CUSTOM_OTELCOL_CONFIG_FILE pattern end-to-end. Add a fixture that emits a span via emit_otel_data and asserts a row in otel_metrics_sum with MetricName = 'traces.span.metrics.calls' (an assert_query.sql/expected.snap pair under data/), which would also catch the temporality/expiry interaction above.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same ask as the earlier open thread on this section (databaseId 3901295654) — leaving this one open alongside it rather than resolving, since it's the same deferred item (a bats fixture asserting a calls/duration row lands in ClickHouse), not a separate piece of work. Tracked in the PR description's "Open item" section.

Verified each claim against the pinned v0.155.0 connector source
(github.com/open-telemetry/opentelemetry-collector-contrib, tag
connector/spanmetricsconnector/v0.155.0) and the real compiled binary,
not just the review's say-so:

- Use `span_metrics`, not `spanmetrics`, as the connector name in new
  examples. Confirmed via `internal/metadata/generated_status.go`:
  Type = "span_metrics" is canonical, DeprecatedType = "spanmetrics" is
  a legacy alias (still works, registered as such in `components`
  output) - new docs shouldn't teach the deprecated name.
- Fixed the `namespace` rationale: confirmed via factory.go that
  `DefaultNamespace = "traces.span.metrics"` is already the default,
  so omitting it does NOT produce bare `calls`/`duration` names as
  previously claimed. Reworded to "pins the default for explicitness."
- Replaced `metrics_expiration` with `series_expiration` for the
  cardinality-cap pairing. Confirmed via config.go doc comments and
  connector.go's ExpireSeries call sites: `metrics_expiration` only
  retires a whole metric object once no spans arrive at all for that
  service+operation; `series_expiration` is the one that evicts
  individual stale dimension combinations from the cardinality-bounded
  map while the metric stays otherwise active - which is what the
  described cardinality-burst scenario actually needs.
- Added `aggregation_temporality: AGGREGATION_TEMPORALITY_DELTA`.
  Cumulative (the connector default) deletes a series' accumulator on
  expiration, so its next occurrence restarts the `calls` counter from
  1 - a real counter reset that HyperDX's own cumulative-rate SQL
  (packages/common-utils/src/core/renderChartConfig.ts, confirmed by
  reading it: `greatest(Value - lagInFrame(Value), 0)`) doesn't detect
  as a reset, it just clamps the delta to 0. For a low-traffic series
  that keeps expiring between spans, every value stays 1 and every
  computed delta is 0 forever - a real "increase" chart under-report,
  not a hypothetical one. Delta temporality passes Value straight
  through with no diffing, sidestepping this entirely, and matches
  Chronosphere's own real-world gateway config.
- Added `histogram.exponential.max_size: 160` to both examples -
  confirmed via connector.go's initHistogramMetrics that the default
  (no `histogram.exponential` set) is an *explicit*-bucket histogram,
  not the exponential one the PR description's ClickHouse
  otel_metrics_exponential_histogram claim depends on. Without this
  field the examples didn't actually deliver the benefit the PR
  describes.
- Broadened the OpAMP example's `datadog` guard comment: it previously
  said "if and only if ENABLE_DATADOG_RECEIVER=true", which is wrong
  for a user who defines their own `datadog` receiver via
  CUSTOM_OTELCOL_CONFIG_FILE instead of the env var - confmap merges
  receiver maps key-by-key regardless of how a key got there.

All five fields verified together against the real compiled binary via
`otelcontribcol validate --config` (both standalone and OpAMP merge
shapes, exit 0). The standalone shape was also actually started (not
just validated): reaches "Everything is ready. Begin running and
processing data." with the span_metrics connector live.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Comment thread packages/otel-collector/README.md Resolved
Comment thread packages/otel-collector/README.md Resolved
Comment thread packages/otel-collector/README.md Resolved
Comment thread packages/otel-collector/README.md Resolved
Comment thread packages/otel-collector/README.md Resolved
…ics section

- Standalone example put the conditional `datadog` guard on
  `metrics: receivers:`, but Datadog *trace* data only reaches
  `span_metrics` via `traces: receivers:` (span_metrics is an exporter
  on the traces pipeline, not metrics). Adding `datadog` to
  metrics.receivers only affects Datadog's own native-metrics ingestion,
  unrelated to span-derived metrics - moved the guard and made
  `traces:` set `receivers:` explicitly instead of leaving it implicit.
  Verified both the bare example (exit 0) and the same shape with a
  real `datadog` receiver defined and added to `traces.receivers`
  (exit 0) against the compiled binary.
- Reworded the "Datadog traces have no other way to produce these"
  claim - HyperDX already computes RED metrics at query time from the
  traces table for any trace source (packages/app/src/serviceDashboard.ts's
  getExpressions/isSpanKindServer). The real motivation is precomputing
  once at the collector plus the exponential-histogram latency series.
- Updated the Connectors summary table's `spanmetrics` row to
  `span_metrics` for consistency with the rest of the section.
- Dropped the two "Chronosphere's own gateway collector" appeals
  (including a dangling ", again" referencing an unshown review
  thread) - both claims already stand on the in-repo/upstream
  reasoning stated right before them.

Also re-verified two disputed bot claims from this same review round
against a freshly rebuilt image before touching anything:
- "`span_metrics` is not a valid connector type, there's no aliasing
  mechanism" - false. `components` output lists `span_metrics` as the
  registered name; `validate --config` accepts it (error messages for
  a bad field even correctly attribute it to
  `'spanmetricsconnector.Config'`); the connector's own factory.go
  calls `xconnector.WithDeprecatedTypeAlias(metadata.DeprecatedType)`,
  which is an explicit aliasing mechanism registering `spanmetrics` as
  a working deprecated alias alongside the canonical `span_metrics`.
  Verified both names validate successfully independently.
- "`series_expiration` is not a real field" - false. Present in the
  pinned v0.155.0 config.go with its own doc comment and validation
  branch, used in connector.go via `ExpireSeries()`; validate accepts
  it, and a deliberate typo of it (along with another made-up field)
  is correctly rejected in the same error message, confirming
  validate's key-checking is real and this field passes because it
  exists, not because validation is lax.
No doc changes made for either - the existing content was already
correct.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Comment thread packages/otel-collector/README.md Resolved
Comment thread packages/otel-collector/README.md Resolved
processors: [memory_limiter, batch]
```

Both shapes above are verified against the real compiled binary via

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — The documented wiring has no smoke test, though the existing CUSTOM_OTELCOL_CONFIG_FILE bats harness covers exactly this case

smoke-tests/otel-collector/custom-pipeline-processors.bats plus smoke-tests/otel-collector/custom-pipeline-config.yaml already do the whole job this section needs: mount a custom config via CUSTOM_OTELCOL_CONFIG_FILE on the otel-collector-custom compose service, emit OTLP data, and assert the resulting rows in ClickHouse. Adding a fixture that emits a span and asserts a traces.span.metrics.calls row lands in otel_metrics_sum would run the documented config against the real binary in CI — which is what the two config errors above needed, since validate on the actual binary rejects both. Copy the existing fixture pattern rather than relying on the manual otelcontribcol validate claim in this paragraph.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same deferred item as the two earlier smoke-test threads on this PR - not a new piece of work, tracked in the PR description's "Open item" section. Leaving open alongside them rather than resolving.

@arj22

arj22 commented Sep 1, 2026 •

Copy link
Copy Markdown
Contributor Author

@wrn14897 could you take a look at this one when you get a chance?

Should the new Computing span metrics section move to its own doc, or is inline fine given other sections are also long?

@wrn14897 wrn14897 self-assigned this Sep 4, 2026
@wrn14897

wrn14897 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

hey @arj22, I will take a look at it. Thanks for the contribution!

Comment on lines +271 to +272
metrics:
receivers: [otlp/hyperdx, span_metrics]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Datadog metrics route is replaced

When a standalone user has configured the Datadog receiver and copies this example, the explicit metrics.receivers list replaces the existing list without preserving datadog, causing native Datadog metrics to stop reaching ClickHouse even though trace-derived metrics continue working.

Fix in Claude Code Fix in Conductor Fix in Cursor Fix in Codex

- gomod:
github.com/open-telemetry/opentelemetry-collector-contrib/connector/routingconnector
v__OTEL_COLLECTOR_VERSION__
- gomod:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — New connector and both documented configs have no automated coverage, though the smoke suite exists precisely for this

smoke-tests/otel-collector/ already boots a standalone collector with CUSTOM_OTELCOL_CONFIG_FILE (the otel-collector-custom service, docker-compose.yaml:79-101) for the stated purpose of verifying "the swap pattern documented in packages/otel-collector/README.md works end-to-end", and metrics.bats already asserts metric rows land in ClickHouse via wait_for_rows. Add a fixture mirroring the documented standalone block plus a bats test that emits a span and waits for rows in otel_metrics_sum (traces.span.metrics.calls) and otel_metrics_exponential_histogram; without it, nothing catches the connector being dropped from the manifest or the config key/field names (span_metrics, series_expiration, aggregation_cardinality_limit) changing on the next OTEL_COLLECTOR_VERSION bump, since the README's "Upgrading the OTel Collector version" checklist doesn't mention re-verifying this section either.

# than adding to it.
receivers: [otlp/hyperdx]
exporters: [clickhouse, span_metrics]
metrics:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — Standalone example silently drops the datadog receiver from the metrics pipeline

The traces: entry carries a "add datadog here too … this list replaces the base one" comment, but metrics: receivers: [otlp/hyperdx, span_metrics] does not — and the Datadog section above (README.md:146-148) tells standalone users to add datadog to the traces, metrics, and logs/in receivers. A Datadog user copying this block replaces their metrics receiver list and silently stops ingesting Datadog native metrics; the prose at README.md:243-245 ("putting datadog on metrics: receivers: instead … does nothing for span-derived metrics") makes that outcome more likely, since it reads as a reason to leave it off. Repeat the same warning comment on the metrics: list.

receivers: [otlp/hyperdx]
exporters: [span_metrics]
processors: [memory_limiter]
metrics/spanmetrics:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — Span-derived metrics never reach the PromQL remote-write path, which the section doesn't mention

Both examples route span metrics only to the ClickHouse exporter. On deployments where PromQL is enabled, the remote-write pipeline receives only otlp/hyperdx (opampController.ts:348-352, and docker/otel-collector/config.standalone.promql.yaml:19-22), so traces.span.metrics.* is queryable from the metrics source but absent from PromQL charts with no error anywhere. Add a sentence noting that a user who wants these series in PromQL must also add span_metrics as a receiver on a pipeline exporting to prometheusremotewrite.

@vercel

vercel Bot commented Sep 6, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
hyperdx-oss Ready Ready Preview Sep 6, 2026 1:34am UTC

Request Review

@kodiakhq
kodiakhq Bot merged commit fc4d587 into ClickHouse:main Sep 6, 2026
28 of 29 checks passed

This branch was successfully deployed

1 active deployment
Preview – hyperdx-oss — 83f867f0 Deployed Sep 6, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants